#include <stdio.h>
#include <string.h>
#include "parser.h"
#include "tokens.h"
#include "lexer.h"
#include "keywords.h"
#include "symtab.h"
#include "codegen.h"

/*
   lexlevel = Nivel léxico
   lexcategory = Categoria ( VAR, PROCEDURE, ETC.)
   lextype = Tipo ( INTEGER, REAL, ETC.)
   gLabel = Label
   lexvarcount = Quantidade de variáveis do nível léxico
   symvarpos[] = Lista de índice para tabela de símbolo
   
*/


extern int symtab[MAXSYMTABENTRIES][MAXSYMTABCOLUMNS];
int lexlevel=0, lexcategory, lextype, gLabel = 1, lexvarcount = 0, symvarpos[128]; 
double gfLexVal[MAXSYMTABENTRIES];

/* program -> block '.' */
void program(void){
    if ( lookahead == PROGRAM ){
      match(PROGRAM);
      match(ID);
      match(';');
    }
    {
      lexcategory = PROGRAM;
    }
    block();
    match('.');
}
/* block -> declscope stmtblock */
void block(void){
   int ii=1;
                                                            
    declscope();
    {
      headGen();
      lexlevel++;
    } 
    stmtblock();
    {
      lexlevel--;
      footGen();
    }
    
}
/* declscope -> uintlist | vardecl | typedecl | funcdecl declscope | procedure declscope */
void declscope(void){
    while ( lookahead == UINT  || lookahead == CONST || lookahead == TYPE  ||
            lookahead == FUNCTION  || lookahead == PROCEDURE  || lookahead == VAR ){
      switch(lookahead){
        case UINT:
 	  uintlist();
        case VAR:
          vardecl();
	  break;
        case CONST:
          constdecl();
	  break;
        case TYPE:
          typedecl();
 	  break;
        case FUNCTION:
	  funcdecl();
	  break;
        case PROCEDURE:
          procedure();
  	  break;
      }
    }
}
/* uintlist -> UINT { ',' UINT } ';' */
void uintlist(void){
    match(UINT);
    if ( lookahead = ',' ){
      match(',');
      match(UINT);
    }
    match(';');
}

/* vardecl -> VAR idlist ':' typespec ';' { idlist ':' typespec ';' } */
void vardecl(void){
   int ii;
   match(VAR);
   {
      lexcategory = VAR;
   }
   idlist();
   match(':');
   typespec();
     {
       for ( ii = 0; ii < lexvarcount ; ii++ )
         symtab[symvarpos[ii]][LEXTYPE] = lextype;
       
     }
   match(';');
   while (lookahead == ID) {
     idlist();
     match(':');
     typespec();
     {
       for ( ii = 0; ii < lexvarcount ; ii++ )
         symtab[symvarpos[ii]][LEXTYPE] = lextype;
       
     }
     match(';');
   }
}

/* idlist -> ID {',' ID} */
void idlist(void){
    {
      lexvarcount = 0;
      symvarpos[lexvarcount] = symtab_insert(lexeme);
      lexvarcount++;
    }
    match(ID);
    while (lookahead == ',') {
        match(',');
        {
          symvarpos[lexvarcount] = symtab_insert(lexeme);
          lexvarcount++;
	}
        match(ID);
    }
}
/* variable -> ID { '[' exprlist ']' | '^'  } */
void variable(void){
   {
     if ( !(symvarpos[0] = symtab_lookup(lexeme)) ) fprintf(stderr, "undefined lexeme %s\n", lexeme);  // Erro
     lextype = symtab[symvarpos[0]][LEXTYPE];
   }
   match(ID);
   while ( lookahead == '[' || lookahead == '^' ){
     if ( lookahead == '[' ){
       match('[');
       exprlist();
       match(']');
     }
     else
       match('^');
   }
}
/* typespec -> INTEGER | REAL | BOOLEAN | TEXT | CHAR */
void typespec(void)
{
    switch (lookahead) {
      case INTEGER:
	      {
	        lextype = INTEGER;
	      }
        match(INTEGER);
        break;
      case REAL:
	      {
	        lextype = REAL;
	      }
        match(REAL);
        break;
      case CHAR:
	      {
	        lextype = CHAR;
	      }
        match(CHAR);
        break;
      case TEXT:
	      {
	        lextype = TEXT;
	      }
        match(TEXT);
        break;
      default:
	{
          lextype = BOOLEAN;
	}
        match(BOOLEAN);
    }
}
/* stmtblock -> BEGIN stmtlist END */
void stmtblock(void)
{
    match(BEGIN);
    {
      lexlevel++;
    }
    stmtlist();
    match(END);
    {
      lexlevel--;
    }
}
/* stmtlist -> stmt {';' stmt} */
void stmtlist(void)
{
    stmt();
    while (lookahead == ';') {
        match(';');
        stmt();
    }
}
/* stmt -> label ( stmtblock | ifstmt | whilestmt | repeat | forstmt | casestmt | assgmnt | procedcall | epsilon ) */
void stmt(void)
{
    if ( lookahead == UINT)
      label();

    switch (lookahead) {
    case BEGIN:
        stmtblock();
        break;
    case ID:
        idstmt();
        break;
    case CASE:
        casestmt();
        break;
    case IF:
        ifstmt();
        break;
    case FOR:
        forstmt();
        break;
    case REPEAT:
        repeat();
        break;
    case WHILE:
        whilestmt();
        break;
    case GOTO:
	gotostmt();
    default:;
    }
}
/* ID prefixed states idstmt
   1) assignment statement: assignm -> variable ":=" expression
   2) procedure call: procedcall -> ID ['('exprlist')']
 */
void idstmt(void)
{
  {
    if ( !(symvarpos[0]=symtab_lookup(lexeme)) ){
      fprintf(stderr, "undefined symbol\n");
    }
  }
  if( symtab[symvarpos[0]][LEXCATEGORY] == VAR || symtab[symvarpos[0]][LEXCATEGORY] == FUNCTION )
     assgnmt();
  else 
     proccall();

}
/* gotostmt -> jmp UINT */
void gotostmt(void){
    match(GOTO);
    {
      if ( !symtab_lookup(lexeme) ){
        fprintf(stderr, "undefined symbol\n");
      }
    }
    match(UINT);
}
/* label -> UINT ':' */
void label(void){
    {
      lexcategory = LABEL;
      symtab_insert(lexeme);
    }
    match(UINT);
    match(':');
}
/* typedecl -> CONST { ID '=' type ';' } */
void typedecl(void){
	{
          lexcategory = TYPE;
	}
        match(TYPE);
	while(lookahead==ID){
		match(ID);
	        match('=');
		type();
		match(';');
	}
}
/*funccall -> FUNCTION ID '(' exprlist ')' */
void funccall(void){
      match(FUNCTION);
      {
        symvarpos[0] = symtab_lookup(lexeme);
	lextype = symtab[symvarpos[0]][LEXTYPE];
      }
      match(ID);
      match('(');
      exprlist();
      match(')');
}
/*type -> FILE OF type | ARRAY '[ simpletype {',' simpletype} ']' OF type */
void type(void){
    switch(lookahead){
       case file: // FILE OF type
	    match(file);
	    match(OF);
	    type();
	    break;
       case ARRAY: // ARRAY '[' simpletype {',' simpletype} ']' OF type
	    match(ARRAY);
	    match('[');
	    simpletype();
	    match(']');
	    match(OF);
	    type();
	    break;
       default:
	    simpletype();
    }
}
//simpletype -> [ '+' | '-' | '\' ] constdef '..' constef  |  '(' idlist ')' | typespec
void simpletype(void){
   switch( lookahead ){
     case ADD:  // CONST .. CONST
     case '\'': //
        constdef();
        match(DDOT);
	constdef();
        break;
     case '(': // '(' idlist ')'
        match('(');
        idlist();
        match(')');
        break;
     default:
        typespec();
   }
}
/* funcdecl -> FUNCTION ID paramlist ':' typespec ';' stmt ';' */
void funcdecl(void){
    match(FUNCTION);
    {
      lexcategory = FUNCTION;
      symvarpos[0] = symtab_insert(lexeme);
    }
    match(ID);
    paramlist();
    match(':');
    typespec();
    {
      symtab[symvarpos[0]][LEXTYPE] = lextype;
    }
    match(';');
    block();
    match(';');
}
/* procedure -> PROCEDURE ID paramlist ';' stmt ';'*/
void procedure(void){
    match(PROCEDURE);
    {
      lexcategory = PROCEDURE;
      symtab_insert(lexeme);
    }
    match(ID);
    paramlist();
    match(';');
    block();
    match(';');
}
/* proccall -> PROCEDURE ID ['(' exprlist ')']  */
void proccall(void){
    match(PROCEDURE);
    {
      if( !(symtab_lookup(lexeme)) ){
        fprintf(stderr, "undefined symbol %s\n", lexeme);
      }/*else{
	printf("\tcall %s\n", lexeme);
      }*/
    }
    match(ID);
    if ( lookahead == '(' ){
      match('(');
      exprlist();
      match(')');
    }
}
/* constdecl -> CONST ID '=' constdef ';' */
void constdecl(void){
    match(CONST);
    while (lookahead == ID) {
        {
	  lexcategory = CONST;
          symvarpos[0] = symtab_insert(lexeme);
        }
	match(ID);
        match('=');
        constdef();
        match(';');
    }
}
/* constdef -> ' ID ' | [+|-] (ID|UINT|FLOAT)  */
void constdef(void){
  int oper = 0;
  int iMyPos;
  if( lookahead=='\'' ) {
    match('\'');
    match(ID);
    match('\'');
    return;
  }
  if( lookahead == ADD ){ // +|- ?
    {
      oper = lexeme[0];
    }
    match(lookahead); 
  }
  {
    gfLexVal[symvarpos[0]] = (oper == '-') ? -(atof(lexeme)) : atof(lexeme); 
  }
  if( lookahead == UINT )
    match(UINT);

  else if( lookahead == FLOAT )
    match(FLOAT);
  
  else{ // ID
    {
       if ( (iMyPos = symtab_lookup(lexeme)) ) gfLexVal[symvarpos[0]] = gfLexVal[iMyPos];
       else fprintf(stderr, "symbol undefined %s", lexeme);
    }   
    match(ID);
  }
}
/*uconst ->   */
void uconst(void){
   int iMyPos;
   if ( lookahead == ID ){
     {
       lextype = symtab[symvarpos[0]][LEXTYPE];
     } 
     match(ID);
   }
   else if ( lookahead == UINT ){
     {
       lextype = INTEGER;
     }
     match(UINT);
   }
   else if ( lookahead == FLOAT ){
     {
       lextype = REAL;
     }
     match(FLOAT);
   }
   else{                         
      match('\'');
      match(ID);  
      match ('\'');
   }
}
/*paramlist -> '(' { (VAR | FUNCTION) varlist ':' typespec ';' } | '(' {PROCEDURE varlist ';' } ')'  */
void paramlist(){
  match('(');
  switch(lookahead){
       case VAR:
       case FUNCTION:
	  match(lookahead);
	  idlist();
          match(':');
	  typespec();
	  break;
       case PROCEDURE:
	  match(PROCEDURE);
	  idlist();
       default:;
  }
  while ( lookahead == ';' ){
    match(';');
    switch(lookahead){
       case VAR:
       case FUNCTION:
	  match(lookahead);
	  idlist();
          match(':');
	  typespec();
	  break;
       case PROCEDURE:
	  match(PROCEDURE);
	  idlist();
       default:;
    }
  }
  match(')');
}
/* ifstmt -> IF expr THEN stmt [ ELSE stmt ] */
void ifstmt(void)
{
    int lbl;
    match(IF);
    expr();
    {
      lbl = gLabel++;
      ifGen(lbl);
    }
    match(THEN);
    stmt();
    if (lookahead == ELSE) {
        match(ELSE);
	{
	  elseGen(lbl);
	  lbl = gLabel++;
	}
        stmt();
    }
    {
      endIfGen(lbl);
    }
}
/* whilestmt -> WHILE expr DO stmt */
void whilestmt(void)
{
    int lbl_beg, lbl_end;
    match(WHILE);
    {
      lbl_beg = gLabel++;
      beginWhileGen(lbl_beg);
    }
    expr();
    {
      lbl_end = gLabel++;
      leaveWhileGen(lbl_end);
    }
    match(DO);
    stmt();
    {
      repeatWhileGen(lbl_beg);
    }
    {
      endWhileGen(lbl_end);
    }
}
/* repeat -> REPEAT stmtlist UNTIL expr */
void repeat(void)
{
    int lbl_beg, lbl_end;
    match(REPEAT);
    {
      lbl_beg = gLabel++;
      beginRepeatGen(lbl_beg);
      lbl_end = gLabel++;
    }
    stmtlist();
    match(UNTIL);
    expr();
    {
      endRepeatGen(lbl_beg, lbl_end);
    }
}
/*
   forstmt -> FOR assgnmt to expr DO stmt
   to -> TO | DOWNTO
 */
void forstmt(void)
{
    int lbl_beg, lbl_end;
    match(FOR);
    {
      lbl_beg = gLabel++;
      beginForGen(lbl_beg);
    }
    assgnmt();
    if (lookahead == TO)
        match(TO);
    else
        match(DOWNTO);
    expr();
    {
      lbl_end = gLabel++;
      leaveForGen(lbl_end);
    }
    match(DO);
    stmt();
    {
      endForGen(lbl_beg, lbl_end);
    }
}
/* assgnmt -> variable ":=" expr  */
void assgnmt(void)
{
    int iMyPos;
    if( symtab[symvarpos[0]][LEXCATEGORY] == FUNCTION ){
       match(ID); 
       lextype = symtab[symvarpos[0]][LEXTYPE];
    }
    else if ( symtab[symvarpos[0]][LEXCATEGORY] == VAR ){
       variable();
    }
    {
      
      iMyPos = symvarpos[0];
    }
    match(ASGNM);
    expr();
    
}
/* casestmt -> CASE expr OF constdef ':' stmtlist { constdef ':' stmtlist ';' } END */
void clause(void)
{
    constdef();
    match(':');
    stmtlist();
    match(';');
}
void casestmt(void)
{
    match(CASE);
    expr();
    match(OF);
    clause();
    while (lookahead == UINT || lookahead == FLOAT || lookahead == ID 
           || lookahead == '\'' || lookahead == ADD  ) {
        clause();
    }
    match(END);
}
/* expr -> E [ relop E ] */
void expr(void)
{
    E();
    if (lookahead == RELOP) {
        match(lookahead);
        E();
    }
}
void exprlist(void){
  expr();
  while ( lookahead == ',' ){
    match(',');
    expr();
  }
}
/* E -> neg T {add T}  */
void E(void)
{
    int oper = 0;
    int first = 1;

    if (lookahead == '-' || lookahead == NOT)
        match(lookahead);
    T();
    while ( lookahead == ADD ) {
	{
          oper = lexeme[0];
	}
        match(lookahead);
        T();
	/*
	Limpa eax
	Verifica se é soma ou subtração
	Coloca o resultado da operação no topo da pilha
	*/
	
	{
	   EGen(&first, oper);
	}
    }
}
/* T -> F { mul F } */
void T(void)
{
    int oper = 0;
    int first = 1;

    F();
    while ( lookahead == MUL ) {
	{
          oper  = lexeme[0];
	}
        match(lookahead);
        F();

	/*
	Limpa eax
	Verifica se é multiplicação ou divisão
	Coloca o resultado da operação no topo da pilha
	*/
	{
	  TGen(&first, oper);
	}
    }
}
/* F -> variable | funcall | cte | '(' expr ')' | NOT F */
void F(void)
{
    switch (lookahead) {
    case ID:
	{
	  if( !(symvarpos[0]=symtab_lookup(lexeme)) )
             fprintf(stderr, "undefined lexeme %s", lexeme);
	  
	}
        if( symtab[symvarpos[0]][LEXCATEGORY] == FUNCTION ){
	   funccall();
	}
	else if ( symtab[symvarpos[0]][LEXCATEGORY] == VAR ){
           variable();
	}
	else{
	   uconst();
	}
	/*{
	  printf("\tmovl $%f, %%eax\n", gfLexVal[symvarpos[0]]);
          printf("\tpush %%eax\n");
	}*/
        break;
    case UINT:
	{
	  uIntGen();
	}
        uconst();
        break;
    case FLOAT:
	{
	   floatGen();
	}
        uconst();
        break;
    case '\'':
        uconst();
	break;
    case BOOL:
        match(BOOL);
        break;
    case NOT:
	match(NOT);
	F();
    default:
        match('(');
        exprlist();
        match(')');
    }
}

